import datetime

from database import db_session

from flask import session

from models import *

from sqlalchemy import exc
from sqlalchemy import or_
from sqlalchemy import func

def listar_fases(nombre_proyecto):
    """Lista todas las fases de un proyecto
    @param nombre_proyecto: recibe el nombre del proyecto
    @return items: lista de fases del proyecto"""
    
    items = []
    proyecto = db_session.query(Proyecto).filter_by( \
    nombre=nombre_proyecto).one()
    for fase in proyecto.fases:
        item = dict(nombre=fase.nombre, descripcion=\
        fase.descripcion, observacion=fase.observacion, \
        estado=fase.estado, orden=fase.orden)

        items.append(item)

    items = sorted(items, key=lambda elemento: elemento['orden'])    
    return items

def validaciones_crear_fases(nombre_proyecto):
    """Verifica que el estado del proyecto se encuentre en Pendiente
    @param nombre_proyecto: indica el nombre del proyecto
    @return error: mensaje de error en caso que el estado del 
                   proyecto no sea pendiente"""
    
    error = "OK"
    proyecto = db_session.query(Proyecto).filter_by(nombre = \
    nombre_proyecto).one()
    if proyecto.estado != "PEN":
        error = "El estado del proyecto debe ser Pendiente"

    return error

def verificar_parametros_crear_fases(nombre_proyecto, nombre, descripcion):
    """Valida los parametros ingresados antes de insertar la fase
    @param nombre_proyecto: indica el nombre del proyecto
    @param nombre: indica el nombre de la fase
    @param descripcion: indica la descripcion de la fase
    @return error: mensaje de error en caso de no ingresar correctamente
                   los parametros"""
    
    error = "OK"
    if nombre == "":
        error = "Se debe ingresar un nombre"
        return error
    if descripcion == "":
        error = "Se debe ingresar una descripcion"
        return error
    
    proyecto = db_session.query(Proyecto).filter_by(nombre = \
    nombre_proyecto).one()

    for fase in proyecto.fases:
        if fase.nombre == nombre:
            error = "Ya existe una fase con ese nombre"
            return error

    return error

def insertar_fase(nombre_proyecto, nombre, descripcion, observacion):
    """Inserta una fase
    @param nombre_proyecto: indica el nombre del proyecto
    @param nombre: indica el nombre de la fase
    @param descripcion: indica la descripcion de la fase
    @param observacion: indica una observacion de la fase"""
    
    try:

        fecha_actual = datetime.now()
        id_usuario = session['id_usuario']
        usuario = db_session.query(Usuario).filter_by(\
        id_usuario=id_usuario).one()
        proyecto = db_session.query(Proyecto).filter_by(\
        nombre = nombre_proyecto).one()
        cant = db_session.query(Fase).filter_by(\
        fk_proyecto = proyecto.id).count()
        
        fase = Fase()
        fase.nombre = nombre
        fase.descripcion = descripcion
        fase.observacion = observacion
        fase.estado = "INA"
        fase.fk_proyecto = proyecto.id
        fase.orden = cant + 1
        fase.fecha_creacion = fecha_actual
        fase.usuario_creacion = proyecto.lider
        fase.fecha_modificacion = fecha_actual
        fase.usuario_modificacion = proyecto.lider
        
        db_session.add(fase)
        db_session.flush()
        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

    return "OK"
    
def validaciones_modificar_fase(seleccionados):
    """ Validaciones realizadas para la modificacion de la fase
    @param seleccionados: lista de fases seleccionadas
    @return error: mensaje de error en caso de no seleccionar alguna fase
                   o selecciono mas de una fase"""
    
    error = "OK"
    if len(seleccionados) == 0: #No selecciono ninguna fase
        error = "Debe seleccionar una fase"

    if len(seleccionados) > 1: #Selecciono mas de una fase
        error = "Debe seleccionar una sola fase"

    return error

def validaciones_parametros_modificar_fase( nombre_proyecto, nombre_fase, \
p_nombre, p_descripcion):
    """Verifica que todos los parametros requeridos fueron ingresados
    @param nombre_proyecto: indica el nombre del proyecto
    @param nombre_fase: indica el nombre de la fase
    @param p_nombre: indica el nombre  
    @param p_descripcion: indica la descripcion
    @return error: mensaje de error en caso de no ingresar correctamente 
                    los datos requeridos"""
    
    error = "OK"
    if p_nombre == "":
        error = "Se debe ingresar un nombre"
        return error
        
    elif p_descripcion == "":
        error = "Se debe ingresar la descripcion"
        return error
    
    if nombre_fase != p_nombre:
        proyecto = db_session.query(Proyecto).filter_by(nombre = \
        nombre_proyecto).one()
        fase = db_session.query(Fase).filter_by(fk_proyecto = \
        proyecto.id).filter( func.lower(Fase.nombre) == func.lower(p_nombre))
        if fase is not None:
            error = "Ya existe el nombre dentro del proyecto"
            return error
    
    return error

def modificar_fase(nombre_proyecto, nombre_fase, p_nombre, \
        p_descripcion, p_observacion):
    """Realiza la accion de modificar el proyecto
    @param nombre_proyecto: indica el nombre del proyecto
    @param nombre_fase: indica el nombre de la fase
    @param p_nombre: indica el nombre  
    @param p_descripcion: indica la descripcion
    @param p_observacion: indica la observacion"""
    
    error = "OK"
    try:
        fecha_actual = datetime.now()
        proyecto = db_session.query(Proyecto).filter_by(nombre = \
        nombre_proyecto).one()
        fase = db_session.query(Fase).filter_by(fk_proyecto = \
        proyecto.id).filter_by(nombre = nombre_fase).one()
        
        
        fase.nombre = p_nombre
        fase.descripcion = p_descripcion
        fase.observacion = p_observacion
        fase.fecha_modificacion = fecha_actual
        fase.usuario_modificacion = proyecto.lider
        
        db_session.flush()
        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

    return error

def buscar_fase(cadena, campo):
    """Realiza la busqueda de las fases de acuerdo al filtro 
    seleccionado (nombre, descripcion, observacion,estado, orden) 
    y devuelve las conincidencias encontradas
    @param cadena: indica el dato que se quiere buscar
    @param campo: indica por cual campo se va a buscar
    @return items: retorna la lista con las coincidencias de la busqueda"""
    
    items = []
    orden = None
    fases = None

    nombre_proyecto = session["nombre_proyecto"]

    proyecto = db_session.query(Proyecto).filter_by(nombre = \
    nombre_proyecto).one()

    if campo == "todos":
        try:
            orden = int(cadena)
        except:
            orden = None
        fases = db_session.query(Fase).filter_by(\
        fk_proyecto=proyecto.id).filter(or_(\
        func.lower(Fase.nombre).contains(cadena),\
        func.lower(Fase.descripcion).contains(cadena),\
        func.lower(Fase.observacion).contains(cadena),\
        func.lower(Fase.estado).contains(cadena),\
        Fase.orden==orden)).all()

    if campo == "nombre":
        fases = db_session.query(Fase).filter_by(fk_proyecto=\
        proyecto.id).filter(func.lower(Fase.nombre).contains(cadena))\
        .all()

    if campo == "descripcion":
        fases = db_session.query(Fase).filter_by(fk_proyecto=\
        proyecto.id).filter(func.lower(Fase.descripcion).\
        contains(cadena)).all()

    if campo == "observacion":
        fases = db_session.query(Fase).filter_by(fk_proyecto=\
        proyecto.id).filter(func.lower(Fase.observacion).\
        contains(cadena)).all()

    if campo == "estado":
        fases = db_session.query(Fase).filter_by(fk_proyecto=\
        proyecto.id).filter(func.lower(Fase.estado).\
        contains(cadena)).all()

    if campo == "orden":
        try:
            orden = int(cadena)
        except:
            return items
        fases = db_session.query(Fase).filter_by(fk_proyecto=\
        proyecto.id).filter(Fase.orden==orden).all()

    for fase in fases:
        item = dict(nombre=fase.nombre, descripcion=fase.descripcion, \
        observacion=fase.observacion, estado=fase.estado, orden=\
        fase.orden)

        items.append(item)

    items = sorted(items, key=lambda elemento: elemento['orden'])
    return items
    
def validaciones_finalizar_fase(nombre_proyecto, seleccionados):
    """ Validaciones realizadas para finalizar la fase
    @param nombre_proyecto: indica el nombre del proyecto
    @param seleccionados: indica la lista de seleccionados de las fases 
                           a finalizar
    @return error: mensaje de error en caso de no seleccionani algna fase
                   o seleccionar mas de una fase para finalizar, tambien 
                   en caso que la fase ya se encuentre finalizada"""
    
    error = "OK"
    if len(seleccionados) == 0: #No selecciono ninguna fase
        error = "Debe seleccionar una fase"
        return error

    if len(seleccionados) > 1: #Selecciono mas de una fase
        error = "Debe seleccionar una sola fase"
        return error
    

    proyecto = db_session.query(Proyecto).filter_by(nombre = \
    nombre_proyecto).one()
    nombre_fase = seleccionados[0]
    fase = db_session.query(Fase).filter_by(fk_proyecto = proyecto.id).\
    filter_by(nombre = nombre_fase).one()
    
    fases_ordenadas = db_session.query(Fase).filter_by(fk_proyecto = proyecto.id).\
    order_by(Fase.orden).all()
    for fase_ordenada in fases_ordenadas:
        if fase_ordenada.orden < fase.orden and fase_ordenada.estado != "FIN":
            error = "Las fases anteriores deben estar finalizadas"
            return error
    
    if fase.estado == "FIN":
        error = "La fase ya se encuentra Finalizada"
        return error
    
    if fase.estado != "ACT":
        error = "La fase debe estar Activa"
        return error

    sw = False    
    for tipo_item in fase.tipos_item:
        items = db_session.query(Item).filter_by(fk_tipo_item = \
        tipo_item.id).all()
        for item in items:
            sw = True
            if item.estado != "BLO" and item.estado != "ELI" and item.estado != "INA" :
                error = "Todos los items de la fase deben de estar Bloqueados"
                return error
    
    if sw == False:
        error = "La fase debe tener asociada al menos un item"
        return error
    
    return error
    
def finalizar_fase( nombre_proyecto, nombre_fase, finalizacion):
    """Finaliza la fase
    @param nombre_proyecto: Indica el nombre del proyecto
    @param nombre_fase: indica el nombre de la fase"""
    
    error = "OK"
    try:
        fecha_actual = datetime.now()
        proyecto = db_session.query(Proyecto).filter_by(nombre = \
        nombre_proyecto).one()
        fase = db_session.query(Fase).filter_by(fk_proyecto = \
        proyecto.id).filter_by(nombre = nombre_fase).one()
        
        fase.estado = "FIN"
        fase.finalizacion = finalizacion
        fase.fecha_modificacion = fecha_actual
        fase.usuario_modificacion = proyecto.lider
        
        db_session.flush()
        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message
    
    return error    

def validaciones_boton_importar_fase(nombre_proyecto):
    """Verifica que el proyecto se encuentre en estado pendiente
    @param nombre_proyecto: Nombre del proyecto seleccionado
    @return error: mensaje de error en caso que la fase no pueda ser
                   importada"""
    
    error = None

    proyecto = db_session.query(Proyecto).filter_by(nombre=\
    nombre_proyecto).one()

    if proyecto.estado != 'PEN':
        return "No se puede importar una fase a un proyecto con estado \
        distinto a Pendiente"

    return error

def listar_fases_a_importar(id_usuario):
    """Devuelve un listado con las fases de todos los proyectos de 
    los cuales es lider
    @param id_usuario: Id del usuario logueado
    @return items: retorna la lista de fases"""
    
    items = []

    usuario = db_session.query(Usuario).filter_by(id_usuario=\
    id_usuario).one()

    proyectos = db_session.query(Proyecto).filter_by(lider=\
    usuario.id).all()

    for proyecto in proyectos:
        for fase in proyecto.fases:
            item = dict(id=fase.id, proyecto=proyecto.nombre, \
            nombre=fase.nombre, descripcion=fase.descripcion, \
            observacion=fase.observacion)

            items.append(item)

    items = sorted(items, key=lambda elemento: elemento['proyecto'])
    return items

def validaciones_importar_fase(seleccionados):
    """Valida que se seleccione al menos una fase
    @param seleccionados: Lista de las fases seleccionadas
    @return error: mensaje de error en caso de no seleccionar uno o mas
                   de una fase a iportar"""
    
    error = None

    if len(seleccionados) == 0: #No selecciono ninguna fase
        return "Debe seleccionar al menos una fase"

    return error

def realizar_importacion_fases(id_usuario, nombre_proyecto, seleccionados):
    """Importa las fases seleccionadas al proyecto actual
    @param id_usuario: Id del usuario logueado
    @param nombre_proyecto: Nombre del proyecto actual
    @param seleccionados: Lista de fases seleccionadas"""
    
    error = None

    usuario = db_session.query(Usuario).filter_by(id_usuario=\
    id_usuario).one()

    proyecto = db_session.query(Proyecto).filter_by(nombre=\
    nombre_proyecto).one()

    numero_fases = db_session.query(Fase).filter_by(fk_proyecto = proyecto.id).count()
    fecha_actual = datetime.now()

    try:
        for elemento in seleccionados:
            fase = db_session.query(Fase).filter_by(id=int(elemento)).one()

            nombre_fase = fase.nombre
            #Si el nombre de la fase ya existe en el proyecto actual
            #se agrega la fecha al final del nombre de la fase importada
            continuar = True
            contador = 1
            while continuar:
                continuar = False
                contador = contador + 1
                for item in proyecto.fases:
                    if nombre_fase == item.nombre:
                        nombre_fase = fase.nombre + str(contador)
                        continuar = True
                        break

            numero_fases = numero_fases + 1

            #Se crea la nueva fase
            nueva_fase = Fase()
            nueva_fase.nombre = nombre_fase
            nueva_fase.descripcion = fase.descripcion
            nueva_fase.observacion = fase.observacion
            nueva_fase.estado = "INA"
            nueva_fase.fk_proyecto = proyecto.id
            nueva_fase.orden = numero_fases
            nueva_fase.fecha_creacion = fecha_actual
            nueva_fase.usuario_creacion = usuario.id
            nueva_fase.fecha_modificacion = fecha_actual
            nueva_fase.usuario_modificacion = usuario.id
            db_session.add(nueva_fase)
            db_session.flush()

            #Se crean los tipos de item de la nueva fase
            for tipoItem in fase.tipos_item:
                nuevo_tipo_item = TipoItem()
                nuevo_tipo_item.nombre = tipoItem.nombre
                nuevo_tipo_item.descripcion = tipoItem.descripcion
                nuevo_tipo_item.observacion = tipoItem.observacion
                nuevo_tipo_item.fk_fase = nueva_fase.id
                nuevo_tipo_item.fecha_creacion = fecha_actual
                nuevo_tipo_item.usuario_creacion = usuario.id
                nuevo_tipo_item.fecha_modificacion = fecha_actual
                nuevo_tipo_item.usuario_modificacion = usuario.id
                db_session.add(nuevo_tipo_item)
                db_session.flush()

                #Se crean los atributos del nuevo tipo de item
                for atributo in tipoItem.atributos:
                    nuevo_atributo = Atributo()
                    nuevo_atributo.nombre = atributo.nombre
                    nuevo_atributo.descripcion = atributo.descripcion
                    nuevo_atributo.observacion = atributo.observacion
                    nuevo_atributo.tipo = atributo.tipo
                    nuevo_atributo.fk_tipo_item = nuevo_tipo_item.id
                    nuevo_atributo.obligatorio = atributo.obligatorio
                    nuevo_atributo.fecha_creacion = fecha_actual
                    nuevo_atributo.usuario_creacion = usuario.id
                    nuevo_atributo.fecha_modificacion = fecha_actual
                    nuevo_atributo.usuario_modificacion = usuario.id
                    db_session.add(nuevo_atributo)
                    db_session.flush()

        db_session.commit()

    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

    return "OK"

def validaciones_intercambiar_fases(nombre_proyecto, seleccionados):
    """Solo puede realizarse el intercambio de fases en un proyecto con 
    estado Pendiente. Ademas, valida que se hayan seleccionado dos 
    fases, y solo dos, del proyecto actual
    @param nombre_proyecto: Nombre del proyecto actual
    @param seleccionados: Lista de fases seleccionadas
    @return error: mensaje de error en caso de que el estado del proyecto 
                   no sea Pendiente, ademas en caso de no seleccionar
                   alguna fase o mas de una fase"""
    
    error = None

    proyecto = db_session.query(Proyecto).filter_by(nombre=\
    nombre_proyecto).one()

    if proyecto.estado != "PEN":
        error = "El estado del proyecto debe ser Pendiente"
        return error

    if len(seleccionados) <= 1: #No selecciono ninguna fase, o solo una
        return "Debe seleccionar dos fases"

    if len(seleccionados) > 2: #Selecciono mas de dos fase
        return "Debe seleccionar solo dos fases"

    return error

def intercambiar_fases(id_usuario, nombre_proyecto, seleccionados):
    """Intercambia el orden de las dos fases recibidas como 
    parametros en la lista seleccionados
    @param id_usuario: Id del usuario logueado
    @param nombre_proyecto: Nombre del proyecto actual
    @param seleccionados: Lista de fases seleccionadas para el 
            intercambio"""
            
    nombre_faseA = seleccionados[0]
    nombre_faseB = seleccionados[1]

    try:
        proyecto = db_session.query(Proyecto).filter_by(nombre=\
        nombre_proyecto).one()

        faseA = db_session.query(Fase).filter_by(nombre=\
        nombre_faseA, fk_proyecto=proyecto.id).one()

        faseB = db_session.query(Fase).filter_by(nombre=\
        nombre_faseB, fk_proyecto=proyecto.id).one()

        aux = faseA.orden
        faseA.orden = faseB.orden
        faseB.orden = aux

        db_session.add(faseA)
        db_session.add(faseB)
        db_session.flush()

        #Se registra la modificacion en el proyecto
        usuario = db_session.query(Usuario).filter_by(id_usuario=\
        id_usuario).one()

        fecha_actual = datetime.now()

        proyecto.usuario_modificacion = usuario.id
        proyecto.fecha_modificacion = fecha_actual

        db_session.add(proyecto)
        db_session.flush()
        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

    return "OK"

def validaciones_eliminar_fases(nombre_proyecto, seleccionados):
    """Solo puede realizarse la eliminacion de fases en un proyecto con 
    estado Pendiente. Ademas, valida que se haya seleccionado al menos
    una fase del proyecto actual
    @param nombre_proyecto: Nombre del proyecto actual
    @param seleccionados: Lista de fases seleccionadas
    @return error: mensaje de error en caso de que el estado del proyecto
                   no sea Pendiente, ademas que se seleccione una o mas 
                   fases"""
    
    error = None

    proyecto = db_session.query(Proyecto).filter_by(nombre=\
    nombre_proyecto).one()

    if proyecto.estado != "PEN":
        error =  "No se puede eliminar una fase de un proyecto con \
        estado distinto a Pendiente"
        return error

    if len(seleccionados) == 0: #No selecciono ninguna fase
        error = "Debe seleccionar al menos una fase"
        return error

    return error

def eliminar_fases(id_usuario, nombre_proyecto, seleccionados):
    """Se eliminan las fases seleccionadas, asi como los tipos de item 
    y los atributos de los mismos. Se actualiza el orden de las demas 
    fases del proyecto
    @param id_usuario: Id del usuario logueado
    @param nombre_proyecto: Nombre del proyecto actual
    @param seleccionados: Lista de fases seleccionadas"""
    
    try:
        usuario = db_session.query(Usuario).filter_by(id_usuario=\
        id_usuario).one()

        proyecto = db_session.query(Proyecto).filter_by(nombre=\
        nombre_proyecto).one()

        for item in seleccionados:
            nombre_fase = item

            fase = db_session.query(Fase).filter_by(nombre=\
            nombre_fase, fk_proyecto=proyecto.id).one()

            for tipo_item in fase.tipos_item:
                for atributo in tipo_item.atributos:
                    #Se eliminan los atributos
                    db_session.delete(atributo)
                    db_session.flush()

                #Se eliminan los tipos de item
                db_session.delete(tipo_item)
                db_session.flush()

            for rol in fase.roles:
                rol_usuarios = rol.usuarios
                for usuario in list(rol_usuarios):
                    #Se elimina la relacion usuario-rol
                    rol.usuarios.remove(usuario)
                    db_session.flush()

                rol_permisos = rol.permisos
                for permiso in list(rol_permisos):
                    #Se elimina la relacion rol-permiso
                    rol.permisos.remove(permiso)
                    db_session.flush()

                db_session.delete(rol)
                db_session.flush()

            #Se elimina la fase
            db_session.delete(fase)
            db_session.flush()

        #Se actualizan los valores de orden de las demas fases del proyecto
        fases = db_session.query(Fase).filter_by(fk_proyecto=\
        proyecto.id).order_by(Fase.orden).all()

        contador = 1

        for fase in fases:
            fase.orden = contador
            contador = contador + 1
            db_session.add(fase)
            db_session.flush()

        #Se actualiza la fecha y usuario que modifico el proyecto
        fecha_actual = datetime.now()

        proyecto.usuario_modificacion = usuario.id
        proyecto.fecha_modificacion = fecha_actual
        db_session.commit()
    except exc.SQLAlchemyError, e:
        db_session.rollback()
        return e.message

    return "OK"
